﻿/*
 * DATAFLOWCORE
 * 
 *

 Copyright 2012 - Mindstorm Multitouch Limited

Author - Bertrand Nouvel

DataFlowCore is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

DataFlowCore is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser Public License for more details.
*/


using MongoDB.Bson;
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Linq;



namespace DFlowCore
{
    public partial class Engine : MarshalByRefObject
    {

        [ServerCommand("node_exists")]
        public BsonDocument NodeExists(BsonDocument q)
        {
            BsonDocument bd = new BsonDocument();
            if (currentDataflow == null)
            {
                EngineSetNewDataflow();
            }
            bd.Add("nodename", q["nodename"].AsString);// when reply are too small they are not transfered
            bd.Add("res", (currentDataflow.Find(q["nodename"].AsString) != null));
            return bd;
        }




        [AutoServerCommand("node_delete")]
        public string NodeDelete(string nodename)
        {
            if (currentDataflow == null)
            {
                throw new System.Exception("No Dataflow selected");
            }


            currentDataflow.DeleteNode(nodename);
            return "ok";
        }

        [ServerCommand("node_create")]
        public BsonDocument NodeNew(BsonDocument q)
        {
            BsonDocument bd = new BsonDocument();
            if (currentDataflow == null)
            {
                EngineSetNewDataflow();
            }

            if (currentDataflow.Find(q["nodename"].AsString) != null)
            {
                bd["error"] = "Node already exists remove it, before reinstantiating it.";
                return bd;
            }

            string[] ntv = q["nodetype"].AsString.Split(':');
            if (!nodetypes.ContainsKey(q["nodetype"].AsString))
            {
                bd["error"] = "No such nodetype.";
                return bd;
            }
            System.Type nt = nodetypes[q["nodetype"].AsString];
            string ctorname = ntv[ntv.Length - 1];
            object nodeinstance = null;
            //object nodeinstance=nt.InvokeMember(ctorname,System.Reflection.BindingFlags.CreateInstance,null,null,new object [] {});
            long sticks = System.DateTime.Now.Ticks;
            try
            {

                nodeinstance = nt.InvokeMember(null,
                    System.Reflection.BindingFlags.DeclaredOnly |
                    System.Reflection.BindingFlags.Public |
                    System.Reflection.BindingFlags.NonPublic |
                    System.Reflection.BindingFlags.Instance |
                    System.Reflection.BindingFlags.CreateInstance, null, null, new object[] { });

            }
            catch (System.Exception e)
            {
                BsonDocument ebd = new BsonDocument("error ", e.ToString());
                return ebd;
            }

            DFlow.Node nodei = nodeinstance as DFlow.Node;
            nodei.name = q["nodename"].AsString;
            if (q.Contains("configuration"))
            {
                nodei.UpdateParameters(q["configuration"].AsBsonDocument);
            }
            currentDataflow.AddNode(nodei);
            bd.Add("res", nodei.IsReady());
            System.Diagnostics.Debug.Print("Instantiation of " + q["nodename"].AsString + " took " + (new TimeSpan(System.DateTime.Now.Ticks - sticks)).TotalSeconds.ToString() + " seconds");
            return bd;
        }




        [AutoServerCommand("node_is_ready")]
        public bool NodeIsReady(string nodename)
        {

            if (currentDataflow == null)
            {
                throw new System.Exception("No dataflow...");
            }

            DFlow.Node n = currentDataflow.Find(nodename);
            if (n == null)
            {
                throw new System.Exception("Node not found...");
            }

            return n.IsReady();
        }



        [AutoServerCommand("node_get_nodetypename")]
        public string NodeGetNodeTypeName(string nodename)
        {
            BsonDocument bd = new BsonDocument();
            if (currentDataflow == null) { throw new System.Exception("No dataflow selected"); }
            DFlow.Node n = currentDataflow.Find(nodename);
            if (n == null) { throw new System.Exception("Selected node not found"); }
            System.Type tp = n.GetType();
            return ((tp.Assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyTitleAttribute), false)[0] as System.Reflection.AssemblyTitleAttribute).Title) + "::" + tp.Name;
        }






        [AutoServerCommand("node_get_config_meta")]
        public List<BsonDocument> NodeGetConfigMeta(string nodename)
        {

            if (currentDataflow == null) { throw new System.Exception("No active dataflow"); }
            DFlow.Node n = currentDataflow.Find(nodename);
            if (n == null) { throw new System.Exception("No such node"); }
            System.Type tp = n.GetType();
            List<BsonDocument> res = new List<BsonDocument>();

            if (tp.GetNestedType("Parameters") != null)
            {
                foreach (System.Reflection.FieldInfo fi in tp.GetNestedType("Parameters").GetFields())
                {
                    BsonDocument bd = new BsonDocument();
                    bd.Add("name", fi.Name);
                    bd.Add("type", DFlowCore.BsonUtils.TranslateTypeInfo(fi.FieldType));
                    bd.Add("metadata", TranslateMetadata(fi));
                    res.Add(bd);
                }
            }

            return res;
        }

        [AutoServerCommand("node_get_config")]
        public List<BsonDocument> NodeGetConfig(string nodename)
        {

            if (currentDataflow == null)
            {
                throw new System.Exception("No active dataflow.");
            }

            DFlow.Node n = currentDataflow.Find(nodename);
            if (n == null)
            {
                throw new System.Exception("No such node");
            }

            System.Type tp = n.GetType();
            List<BsonDocument> res = new List<BsonDocument>();

            if (tp.GetNestedType("Parameters") != null)
            {
                foreach (System.Reflection.FieldInfo fi in tp.GetNestedType("Parameters").GetFields())
                {
                    BsonDocument bd = new BsonDocument();
                    bd.Add("name", fi.Name);
                    bd.Add("value", DFlowCore.BsonUtils.BsonEncode(fi.GetValue(n.GetParameters()), fi.FieldType));
                    res.Add(bd);
                }
            }

            return res;
        }

        [ServerCommand("node_set_config")]
        public BsonDocument NodeSetConfig(BsonDocument q)
        {
            if (currentDataflow == null)
            {
                BsonDocument bd = new BsonDocument();
                bd.Add("error", "No Dataflow selected");
                bd.Add("msg", "No Dataflow selected");
                return bd;
            }

            DFlow.Node n = currentDataflow.Find(q["nodename"].AsString);
            if (n == null)
            {
                BsonDocument bd = new BsonDocument();
                bd.Add("error", "No such node : " + q["nodename"].AsString);
                return bd;
            }

            System.Type tp = n.GetType();


            if (tp.GetNestedType("Parameters") != null)
            {
                if (q["configuration"].IsBsonArray)
                {
                    BsonArray resp = q["configuration"].AsBsonArray;
                    BsonDocument d = new BsonDocument();
                    for (int i = 0; i < resp.Count; i++)
                    {
                        d.Add(resp[i].AsBsonDocument["name"].AsString, resp[i].AsBsonDocument["value"]);
                    }

                    if (forwardExceptions)
                    {
                        try
                        {
                            n.UpdateParameters(d);
                        }
                        catch (System.Exception se)
                        {
                            new BsonDocument("error", se.ToString());
                        }
                    }
                    else
                    {
                        n.UpdateParameters(d);
                    }

                }
                if (q["configuration"].IsBsonDocument)
                {
                    if (forwardExceptions)
                    {
                        try
                        {
                            n.UpdateParameters(q["configuration"].AsBsonDocument);
                        }
                        catch (System.Exception se)
                        {
                            new BsonDocument("error", se.ToString());
                        }
                    }
                    else
                    {
                        n.UpdateParameters(q["configuration"].AsBsonDocument);
                    }

                }
            }

            return new BsonDocument("res", "ok");
        }

        [ServerCommand("node_outwire_list")]
        public BsonDocument NodeOutWireList(BsonDocument q)
        {
            if (currentDataflow == null)
            {
                BsonDocument bd = new BsonDocument();
                bd.Add("error", "No Dataflow selected");
                bd.Add("msg", "No Dataflow selected");
                return bd;
            }


            DFlow.Node n = currentDataflow.Find(q["nodename"].AsString);
            if (n == null)
            {
                return new BsonDocument("err", "node not found");
            }
            System.Type t = n.GetType();

            BsonArray res = new BsonArray();

            foreach (DFlow.Wire w in n.GetOutboundWires())
            {
                BsonDocument wiredoc = new BsonDocument();
                wiredoc.Add("srcnode", w.srcnode.name);
                wiredoc.Add("dstnode", w.dstnode.name);
                wiredoc.Add("srcpin", w.srcpin);
                wiredoc.Add("dstpin", w.dstpin);
                wiredoc.Add("datatype", "unspecified");
                res.Add(wiredoc);
            }

            return new BsonDocument("res", res);
        }



        [ServerCommand("node_outnetwire_list")]
        public BsonDocument NodeOutNetWireList(BsonDocument q)
        {
            if (currentDataflow == null)
            {
                BsonDocument bd = new BsonDocument();
                bd.Add("error", "No Dataflow selected");
                bd.Add("msg", "No Dataflow selected");
                return bd;
            }


            DFlow.Node n = currentDataflow.Find(q["nodename"].AsString);
            if (n == null)
            {
                return new BsonDocument("err", "node not found");
            }
            System.Type t = n.GetType();

            BsonArray res = new BsonArray();

            foreach (DFlow.NetWire w in n.GetOutboundNetWires())
            {
                BsonDocument wiredoc = new BsonDocument();
                wiredoc.Add("srcnode", w.srcnode.name);
                wiredoc.Add("dstnode", w.dstnode);
                wiredoc.Add("dsthost", "unspecified");
                wiredoc.Add("srcpin", w.srcpin);
                wiredoc.Add("dstpin", w.dstpin);
                wiredoc.Add("id", w.id);
                wiredoc.Add("datatype", "unspecified");
                res.Add(wiredoc);
            }

            return new BsonDocument("res", res);
        }



        [ServerCommand("node_pin_get_current_value")]
        public BsonDocument NodePinGetCurrentValue(BsonDocument q)
        {
            if (currentDataflow == null)
            {
                BsonDocument bd = new BsonDocument();
                bd.Add("error", "No Dataflow selected");
                bd.Add("msg", "No Dataflow selected");
                return bd;
            }


            DFlow.Node n = currentDataflow.Find(q["nodename"].AsString);
            if (n == null)
            {
                return new BsonDocument("err", "camera not found");
            }
            System.Type t = n.GetType();
            System.Reflection.FieldInfo fi = t.GetField(q["pinname"].AsString);


            if (fi.GetCustomAttributes(typeof(DFlow.OutputPin), false).Length != 0)
            {
                return new BsonDocument("res", DFlowCore.BsonUtils.BsonEncode(fi.GetValue(n), fi.FieldType));
            }


            return new BsonDocument("err", "pin not found");
        }

        [AutoServerCommand("wire_create")]
        public string WireCreate(string srcnodename, string srcpinname, string dstnodename, string dstpinname, string Dataflow_name = null)
        {
            DFlow.Dataflow p = currentDataflow;
            if (Dataflow_name != null)
            {
                p = masterDataflows.Find(tp => tp.name == Dataflow_name);
            }


            BsonDocument bd = new BsonDocument();
            DFlow.Wire w = null;
            DFlow.Node srcnode = p.Find(srcnodename);
            DFlow.Node dstnode = p.Find(dstnodename);

            if (srcnode == null)
            {
                throw new System.ArgumentException(srcnodename);
            }

            if (dstnode == null)
            {
                throw new System.ArgumentException(dstnodename);
            }
            w = new DFlow.Wire(srcnode, srcpinname, dstnode, dstpinname);
            srcnode.AddWire(w);

            return "ok";
        }




        [AutoServerCommand("wire_delete")]
        public string WireDelete(string srcnodename, string dstnodename, string srcpinname, string dstpinname)
        {
            //DFlow.Wire w = null;
            DFlow.Node srcnode = currentDataflow.Find(srcnodename);
            DFlow.Node dstnode = currentDataflow.Find(dstnodename);

            if (srcnode == null) { throw new System.Exception("Input node not found"); }
            if (dstnode == null) { throw new System.Exception("Output node not found"); }

            IEnumerator<DFlow.Wire> rw = srcnode.GetOutboundWires().Where(tw => ((tw.srcnode == srcnode) && (tw.dstnode == dstnode) && (tw.srcpin == srcpinname) && (tw.dstpin == dstpinname))).GetEnumerator();
            rw.MoveNext();
            srcnode.RemoveWire(rw.Current);

            return "ok";
        }


        [AutoServerCommand("node_update_inputs")]
        public BsonDocument UpdateInputs(string nodename, string Dataflow_name = null)
        {
            DFlow.Dataflow p = currentDataflow;
            if (Dataflow_name != null)
            {
                p = masterDataflows.Find(tp => tp.name == Dataflow_name);
            }

            throw new NotImplementedException();

            //            return new BsonDocument("res","ok");
        }


        [AutoServerCommand("node_fetch_outputs")]
        public BsonDocument UpdateOutputs(string nodename, string Dataflow_name = null)
        {
            DFlow.Dataflow p = currentDataflow;
            if (Dataflow_name != null)
            {
                p = masterDataflows.Find(tp => tp.name == Dataflow_name);
            }

            //if (p == null) {  }

            DFlow.Node cn = p.Find(nodename);

            BsonDocument res = new BsonDocument();
            foreach (System.Reflection.FieldInfo fi in cn.ListOutputPins())
            {
                res.Add(fi.Name, DFlowCore.BsonUtils.BsonEncode(fi.GetValue(cn), fi.FieldType));
            }

            return res;
        }



    }
}